<?php
/**
 * Description of DAOGenerator
 *
 * @author BloumGenerator
 */
class DAOGenerator {
    
    private $tableMeta;
    private $parentTbMeta;
    private $templateDAO;
    private $attPK;
    private $params;

    function __construct($tableMeta, $params) {
        $this->tableMeta = $tableMeta;
        $this->params = $params;
        $this->parentTbMeta = null;
        
        if(!file_exists(Constantes::$TEMPLATES_DAO_DIR."DAO.bloum"))
            throw new Exception ("Template DAO Not Found!!!");

        $this->templateDAO = file_get_contents(Constantes::$TEMPLATES_DAO_DIR."DAO.bloum");
        $this->searchPK();
        //$this->generate();

    }

    public function searchPK(){
        foreach ($this->tableMeta->getAttributes() as $att) {
            if($att->isPrimaryKey()){
                $this->attPK[$att->getName()] = $att;
            }
        }
    }

    public function generate(){
        $this->templateDAO = str_replace(Constantes::$TPL_BEAN_NAME_UPPER, ucfirst($this->tableMeta->getName()), $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_BEAN_NAME_LOWER, lcfirst($this->tableMeta->getName()), $this->templateDAO);

        $this->generateExtends();
        $this->generatePkValues();
        $this->generateUpdate();
        $this->generateInsert();
        $this->generateDelete();
        $this->generateListParam();
        $this->generateSetValues();
        $this->generateRelExternal();

        return $this->templateDAO;
    }

    public function generateExtends(){

        $numExt = 0;
        $extends = "";
        $saveParent = "";
        $joinParent = "";
        $setParent = "";
        $constructParent = "";

        foreach ($this->tableMeta->getFKReferences() as $fk) {
            if($fk->isExtends()){
                $numExt++;

                $constructParent = "parent::__construct();";

                $this->parentTbMeta = $fk->getTableReference();

                $nameParent = ucfirst($this->parentTbMeta->getName());
                $tbName = $this->tableMeta->getName();
                
                $extends = "extends ".$nameParent."DAO";                
                $joinParent = "INNER JOIN ".$this->parentTbMeta->getName()." pr ON ";
                
                $attsFk = $fk->getAttributes();
                $attsRef = $fk->getReferences();
                
                for ($id = 0; $id < count($attsFk); $id++) {                    
                    $atFk = $attsFk[$id]->getName();
                    $atRef = $attsRef[$id]->getName();
                    
                    $joinParent .= "pr.$atRef = tb.$atFk";
                    
                    if($id < ( count($attsFk)-1 ))
                        $joinParent .= " AND ";
                    
                }
                
                $setParent = "$".$tbName."->set".$nameParent."( new ".$nameParent."Bean(:rowsParent) );";
                $rowsParent = "";
                $setParentExternal = "";
                $attsTbReference = $this->parentTbMeta->getAttributes();
                $attstbRefKeys = array_keys($attsTbReference);
                
                for ($i = 0; $i < count($attstbRefKeys); $i++) {
                    $name = $attstbRefKeys[$i];

                    if(!$attsTbReference[$name]->isForeignKey()){
                        
                        if(strlen(trim($rowsParent)) > 0)
                            $rowsParent .= ', ';
                        
                        $rowsParent .= '$row[\''.$name.'\']';                    
                        
                    
                    }else{                        
                        $nameClass = str_replace("ID", "", $name);                        
                        $setParentExternal .= "\n\t\t\t\t$".$tbName.'->set'.ucfirst($nameClass).'($this->'.$nameClass.'DAO->buscarPorID($row[\''.$name.'\']));';
                    }
                    
                }
                
                $setParent = str_replace(":rowsParent", $rowsParent, $setParent).$setParentExternal;
                
                $saveParent = "$".$tbName."->set".$nameParent."(parent::salvar($".$tbName."->get".$nameParent."()));";
            }

            if($numExt > 1){
                throw new Exception("Erro, Herança Multipla Não Suportado!!!");
            }
        }
        
        $saveParent .= "\n";

        $this->templateDAO = str_replace(Constantes::$TPL_BEAN_CONSTRUCT_PARENT, $constructParent, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_BEAN_EXTENDS, $extends, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_JOIN_PARENT, $joinParent, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_SAVE_PARENT, $saveParent, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_SET_PARENT, $setParent, $this->templateDAO);
    }

    public function generateInsert(){
        $attrs = $this->tableMeta->getAttributes();
        $attrsKeys = array_keys($attrs);
        $columns = "";
        $values = "";

        for ($i = 0; $i < count($attrsKeys); $i++) {

            $columns .= $attrsKeys[$i];
            $values .= ":".$attrsKeys[$i];

            if($i < ( count($attrsKeys)-1)){
                $columns .= ",";
                $values .= ",";
            }

        }

        $this->templateDAO = str_replace(Constantes::$TPL_DAO_INSERT_COLUMNS, $columns, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_INSERT_VALUES, $values, $this->templateDAO);

    }

    public function generateUpdate(){
        $attrs = $this->tableMeta->getAttributes();
        $attrsKeys = array_keys($attrs);
        $update = "";

        for ($i = 0; $i < count($attrsKeys); $i++) {


            $attMeta = $attrs[$attrsKeys[$i]];

            if($attMeta->isPrimaryKey())
                continue;

            $name = $attMeta->getName();
            $update .= $name . " = :" . $name;
            if($i < ( count($attrsKeys)-1)){
                $update .= ",\n\t\t\t\t\t\t";
            }
        }

        $this->templateDAO = str_replace(Constantes::$TPL_DAO_UPDATE_COLUMNS, $update, $this->templateDAO);
        $returnUpdate = "";
        if(strlen(trim($update)) <= 0){
            $returnUpdate = "return $".$this->tableMeta->getName().";\n";
            $beginUpdate = stripos($this->templateDAO, Constantes::$TPL_DAO_BEGIN_UPDATE);
            $endUpdate = stripos($this->templateDAO, Constantes::$TPL_DAO_END_UPDATE)+strlen(Constantes::$TPL_DAO_END_UPDATE);
            
            $part1 = substr($this->templateDAO, 0, $beginUpdate);
            $part2 = substr($this->templateDAO, $endUpdate);
            
            $this->templateDAO = $part1.$part2;
        }else{
            $this->templateDAO = str_replace(Constantes::$TPL_DAO_BEGIN_UPDATE, "", $this->templateDAO);    
            $this->templateDAO = str_replace(Constantes::$TPL_DAO_END_UPDATE, "", $this->templateDAO);        
        }
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_RETURN_UPDATE, $returnUpdate, $this->templateDAO);    
    }

    public function generateDelete() {
        $parentDelete = "";
        if($this->parentTbMeta != null){
            $parentDelete = "parent::excluir(:pks);";
            $pks = "";
            $attrsKeys = array_keys($this->attPK);
            
            for ($i = 0; $i < count($attrsKeys); $i++) {
                $att = $this->attPK[$attrsKeys[$i]];
                $name = "$".$att->getName();
                
                $pks .= $name;
                if($i < ( count($attrsKeys)-1 )){
                    $pks .= ", ";
                }
            }
            
            $parentDelete = str_replace(":pks", $pks, $parentDelete);
            
            $beginDelete = stripos($this->templateDAO, Constantes::$TPL_DAO_BEGIN_DELETE);
            $endDelete = stripos($this->templateDAO, Constantes::$TPL_DAO_END_DELETE)+strlen(Constantes::$TPL_DAO_END_DELETE);
            
            $part1 = substr($this->templateDAO, 0, $beginDelete);
            $part2 = substr($this->templateDAO, $endDelete);
            
            $this->templateDAO = $part1.$part2;
            
        }else{
            $this->templateDAO = str_replace(Constantes::$TPL_DAO_BEGIN_DELETE, "", $this->templateDAO);    
            $this->templateDAO = str_replace(Constantes::$TPL_DAO_END_DELETE, "", $this->templateDAO);        
        }
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PARENT_DELETE, $parentDelete, $this->templateDAO);    
    }
    
    public function generatePkValues(){

        $tableName = $this->tableMeta->getName();

        $pkWhere = "";
        $pkBuscar = "";
        $pkInitParam = "";
        $pkBind = "";
        $pkIfUpdate = "";
        $pkTotal = "";
        $pkIfNotExist = "";
        $pkSetLastInserted = "";
        $beginIfLastInserted = "";
        $endIfLastInserted = "";

        if($this->parentTbMeta != null)
            $pkIfExist = '$this->buscarPorID(:pk) != null';

        $attrsKeys = array_keys($this->attPK);

        for ($i = 0; $i < count($attrsKeys); $i++) {
            $att = $this->attPK[$attrsKeys[$i]];
            $name = $att->getName();
            
            $pkWhere .= $name." = :".$name;
            $pkBuscar .= "tb.".$name." = $".$name;
            $pkInitParam .= "$".$name." = -1";
            $pkBind .= '$statment->bindParam(\':'.$name.'\', $'.$name.');';

            if($this->parentTbMeta == null){
                $pkIfUpdate .= "$".$tableName."->get".ucfirst($name)."() != null && $".$tableName."->get".ucfirst($name)."() > 0 ";
                $pkIfNotExist .= " $".$tableName."->get".ucfirst($name)."() == null || $".$tableName."->get".ucfirst($name)."() <= 0 ";                
                $pkSetLastInserted .= " $".$tableName."->set".ucfirst($name).'($this->conexao->lastInsertId());';
            }else{
                if(strlen($pkIfUpdate) > 0)
                    $pkIfUpdate .= ", ";
                $pkIfUpdate .= "$".$tableName."->get".ucfirst($name)."()";
            }
                
            
            $pkTotal = $name;
            
            if($i < ( count($this->attPK)-1 )){
                $pkWhere .= " AND ";
                $pkBuscar .= " AND ";
                $pkIfUpdate .= "\n\t\t\t && ";
                $pkIfNotExist .= "\n\t\t\t || ";
                $pkSetLastInserted .= "\n\t\t\t\t";
                $pkInitParam .= ", ";
                $pkBind .= "\n\t\t\t";
            }
        }

        if($this->parentTbMeta != null){
            $pkIfExist = '$this->buscarPorID(:pk) != null';
            $pkIfUpdate = str_replace(":pk", $pkIfUpdate, $pkIfExist);

            $beginIf = stripos($this->templateDAO, Constantes::$TPL_DAO_BEGIN_IF_LAST_INSERTED);
            $endIf = stripos($this->templateDAO, Constantes::$TPL_DAO_END_IF_LAST_INSERTED)+strlen(Constantes::$TPL_DAO_END_IF_LAST_INSERTED);

            $part1 = substr($this->templateDAO, 0, $beginIf);
            $part2 = substr($this->templateDAO, $endIf);

            $this->templateDAO = $part1.$part2;

        }else{
            $this->templateDAO = str_replace(Constantes::$TPL_DAO_BEGIN_IF_LAST_INSERTED, $beginIfLastInserted, $this->templateDAO);
            $this->templateDAO = str_replace(Constantes::$TPL_DAO_END_IF_LAST_INSERTED, $endIfLastInserted, $this->templateDAO);
        }

        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_IF_UPDATE, $pkIfUpdate, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_IF_NOT_EXIST, $pkIfNotExist, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_SET_LAST_INSERTED, $pkSetLastInserted, $this->templateDAO);
        
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_WHERE, $pkWhere, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_BUSCAR, $pkBuscar, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_INIT_PARAM, $pkInitParam, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_BIND, $pkBind, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_PK_TOTAL, $pkTotal, $this->templateDAO);
    }

    public function generateListParam(){

        $listParam = "";
        $listParamInit = "";
        $whereTest = "";

        $this->generateListParamByList($this->tableMeta->getAttributes(), "tb", $listParam, $listParamInit, $whereTest);

        if($this->parentTbMeta != null && count($this->parentTbMeta->getAttributes()) > 0)
            $this->generateListParamByList ($this->parentTbMeta->getAttributes(), "pr", $listParam, $listParamInit, $whereTest);


        $this->templateDAO = str_replace(Constantes::$TPL_LIST_PARAM_INIT, $listParamInit, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_LIST_PARAM, $listParam, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_WHERE_TEST, $whereTest, $this->templateDAO);
        $this->templateDAO = str_replace(",,", ",", $this->templateDAO); #gambiarra!
        
    }

    public function generateListParamByList($attrs, $prefixTable = "tb", &$listParam = "", &$listParamInit = "", &$whereTest = ""){

        $attrsKeys = array_keys($attrs);
        
        for ($i = 0; $i < count($attrsKeys); $i++) {
            $name = $attrsKeys[$i];

            if(!$attrs[$name]->isPrimaryKey() && $attrs[$name]->isImportant()){

                if(strlen(trim($listParam)) > 0){
                    $listParam .= ", ";
                }

                if(strlen(trim($listParamInit)) > 0){
                    $listParamInit .= ", ";
                }

                $listParam .= "$$name";

                $type = $attrs[$name]->getType();
                $default = $attrs[$name]->getDefault();

                $value = '""';
                if(Util::isTypeNumeric($type)){
                    $value = "-1";
                    if(strlen($default))
                        $value = $default;

                    $whereTest .= "if (isset($$name) && $$name > 0) {\n\t\t\t\$where = \$where . \" AND $prefixTable.$name = $$name\";\n\t\t}\n\t\t";

                }else{
                    $value = "\"\"";
                    $whereTest .= "if (isset($$name) && strlen($$name) > 0) {\n\t\t\t\$where = \$where . \" AND $prefixTable.$name = '$$name'\";\n\t\t}\n\t\t";
                }

                $listParamInit .= "$".$name." = ".$value;
            }

        }
        
    }


    public function generateSetValues(){
        $attrs = $this->tableMeta->getAttributes();
        $tableName = $this->tableMeta->getName();
        $attrsKeys = array_keys($attrs);
        $set = "";

        for ($i = 0; $i < count($attrsKeys); $i++) {
            $name = $attrsKeys[$i];

            if(!$attrs[$name]->isForeignKey())
                $set .= "$".$tableName."->set".ucfirst($name).'($row[\''.$name.'\']);'."\n\t\t\t\t";
        }

        $this->templateDAO = str_replace(Constantes::$TPL_DAO_SET_VALUES, $set, $this->templateDAO);

    }

    public function generateRelExternal(){

        $tableName = $this->tableMeta->getName();
        $set = "";
        $dao = "";
        $initDao = "";

        foreach ($this->tableMeta->getAttributes() as $attMeta) {
            $name = $attMeta->getName();

            if(!$attMeta->isPrimaryKey() && $attMeta->isForeignKey()){

                if(stripos($name, "ID"))
                    $name = str_replace ("ID", "", $name);

                $dao .= "protected $".$name."DAO;\n\t";
                $initDao .= '$this->'.$name.'DAO = new '.ucfirst($name)."DAO();\n\t\t";
                $set .= "$".$tableName."->set".ucfirst($name).'($this->'.$name.'DAO->buscarPorID($row[\''.$name.'ID\'],true));'."\n\t\t\t\t\t";

            }

            
        }

        $this->templateDAO = str_replace(Constantes::$TPL_DAO_DAOS_EXTERNAL, $dao, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_INIT_DAOS_EXTERNAL, $initDao, $this->templateDAO);
        $this->templateDAO = str_replace(Constantes::$TPL_DAO_REL_EXTERNAL, $set, $this->templateDAO);
    }

    public function generateFile($generate = true){

        if($generate)
            $this->generate();

        $dirDAO = "temp_gen/".$this->params[Constantes::$PARAM_PROJECT_NAME]."/dao/";

        if(!file_exists($dirDAO))
            mkdir ($dirDAO, 0755,true);

        $fp = fopen($dirDAO.ucfirst($this->tableMeta->getName())."DAO.class.php", "w+");
        fwrite($fp, $this->templateDAO);
        fclose($fp);
    }
}
?>
